
--[[
	消息处理
    房间管理

    desk = {
        service = service,
        id = tid,
        rid = rid,
        rtype = rtype,
        user_num = 0, 总人数
        seat_num = 0, 在座位上的人数
         }
--]]
local cjson = require("cjson")
local table_insert = table.insert

---------------------------------------------------------
-- class
---------------------------------------------------------
local Handler = class("Handler")

function Handler:ctor(name)

    self.start_time = tostring(g.time()) --初始桌子id
    self.index_id = 100
    --room -> desk
    g.user_desk = {} --{uid-> {desk_id}} 玩家所在的房间
    self.lock_desk = false --是否可游戏的锁
    self.closed = false --是否关闭游戏

    self:register()
end

function Handler:register()

    g.register("C2_TS_enter", handler(self, self.reqEnterDesk)) --进入桌子
    g.register("C2_TS_online", handler(self, self.reqOnline)) --在线信息
    g.register("send_table", handler(self, self.sendTable))
    g.register("call_table", handler(self, self.callTable))
    g.register("user_leave", handler(self, self.onUserLeave)) --玩家离开桌子
    g.register("get_user_desks", handler(self, self.onGetUserDesks)) --取玩家所在桌子
    g.register("disconnect", handler(self, self.onDisconnect)) --离线了
    g.register("update_desk", handler(self, self.onUpdateDesk)) --更新桌子信息
    g.register("close_game", handler(self, self.onClose)) --关闭游戏
    g.register("kick_user", handler(self, self.onKickUser))
    g.register("user_login", handler(self, self.onUserLogin)) --玩家登录了

    g.register("C2_TS_enter_game", handler(self, self.reqEnterGame))



    g.defCallback(self, self.defaultCallback)

    --重新加载配置
    if g.room_cfg then
        g.fork(function ()
            self:loadConfig()
        end)
    end

    self:loadTexasConfig()
end

function Handler:loadTexasConfig()
    local config_path = g.getenv("root") .. "config/" --配置文件所在目录
    local path = config_path.."robot_num_cfg.lua"
    local robot_num_cfg = dofile(path)
    for _, v in pairs(robot_num_cfg) do
        v.setting = cjson.decode(v.setting)
    end
    self.robot_num_cfg = {}
    for k, v in pairs(robot_num_cfg) do
        --g.log:debug("loadTexasConfig",v)
        if not self.robot_num_cfg[v.time] then
            self.robot_num_cfg[v.time] = {}
        end
        local setting = v.setting
        for i, j in pairs(setting) do
            local rid = math.floor(j.rid)
            self.robot_num_cfg[v.time][rid] =  math.floor(j.num)
        end
    end
end


----创建一个测试桌子
--function Handler:createChessDesk(rid, rtype)
--    rtype = rtype or 0
--    local tid = self:createDeskId(rid, rtype)
--    local service = g.newService("games.chess.service", tid, rid, rtype)
--    if not service then
--        g.log:err("newService games.texas.service", tid, rid, rtype)
--        return
--    end
--    --桌子对象
--    g.desks[tid] = {service = service,
--                    tid = tid, rid = rid,
--                    rtype = rtype,
--                    user_num = 0,
--                    seat_num = 0,
--                    create_time = g.time(),
--                    robot_num = 0,
--    }
--    g.call(service, "start")
--    return g.desks[tid]
--end


function Handler:onStart()
    self:loadConfig()

    --查看是否存在异常金币归还用户
    --g.timeout(5,function()
    --    self:checkTableChip()
    --end)

    --定时清理无人桌子, 无人桌子不能太多
    g.loop(g.const.room.clear_desk_time, function()
        self:clearIdleDesk()
    end)
end

--加载配置
function Handler:loadConfig()
    print("begin loadConfig")
    --读取房间配置
    g.room_cfg = g.dp:getRoomConfig() -- (room_id, cfg)
    g.sendConfig("set", "room_cfg", g.room_cfg) --让所有桌子共享配置
    --g.sendConfig("set", "blind_cfg", g.blind_cfg)

    --g.room_cfg_snooker = {{
    --                          id = 1,
    --                          seat_count = 2,
    --                          level = 1,
    --                          sid = 2,
    --                          min_buy = 0,
    --                          max_buy = 100,
    --                          max_limit = 1000000,
    --                          gtype = 1,
    --                          cost = 100,
    --                      },
    --                      {
    --                          id = 2,
    --                          seat_count = 2,
    --                          level = 1,
    --                          sid = 2,
    --                          min_buy = 0,
    --                          max_buy = 200,
    --                          max_limit = 1000000,
    --                          gtype = 1,
    --                          cost = 100,
    --                      },
    --                      {
    --                          id = 3,
    --                          seat_count = 2,
    --                          level = 1,
    --                          sid = 2,
    --                          min_buy = 0,
    --                          max_buy = 300,
    --                          max_limit = 1000000,
    --                          gtype = 1,
    --                          cost = 100,
    --                      },
    --}
    g.room_cfg_snooker = g.dp:getSNRoomConfig()
    g.sendConfig("set", "room_cfg_snooker", g.room_cfg_snooker) --让所有桌子共享配置

    print("end loadConfig")
end

--查看是否存在异常金币归还用户(弃用
function Handler:checkTableChip()
    --普通场才会有金币
    local tids = g.dp:getTables()
    g.log:debug("checkTableChip start:", tids)
    for _, tid in pairs(tids) do
        local users = g.dp:getTableUsers(tid)
        for uid, str in pairs(users) do
            local moneys = cjson.decode(str)
            local ok = g.dp:addUserMoney(uid, moneys)
            if ok then
                g.log:warn("checkTableChip user addUserMoney:", uid, moneys)
                self:writeChipLog(uid, tid, moneys, g.const.chip.s_node_restart, "node_restart_back")
                g.dp:delTableUserMoney(tid, uid)
                --通知用户更新金币
                local data = {
                    msg = cjson.encode({event="update_user"})
                }
                local pos = g.dp:getUserPos(uid)
                if pos then
                    g.message:sendNodeManager(pos.nodefullname, "n_send_user", "TS_2C_eventBc", data)
                    --print("333333333333", tostring(data))
                end
            end
        end
        g.dp:delTables(tid)
    end
    --local nid = g.getNodeId()
    --local tids = g.dp:getNodeTables(nid)
    --g.log:debug("checkTableChip end:", tids)
end

function Handler:writeChipLog(uid, tid, moneys, mode, desc)
    local flag = g.const.chip.s_f_add --增加或减少的标记
    local money = g.dp:getUserMoney(uid)
    local game_type = 2 --普通场
    local ltype = 0
    --写流水
    local data = {
        mid = uid,
        lmode = mode,
        lremark = 0,
        ldesc = desc,
        tid = tid,
        ltype = ltype,
        game_type = game_type,
        lflag = flag,
        ltime = g.getMsec(),
    }
    local ltype_name = {
        chips = 1,
        bonus = 2,
        winning = 3,
        gmoney = 4,
    }
    for k, v in pairs(moneys) do
        data.ltype = ltype_name[k]
        if v > 0 then
            data.lflag = g.const.chip.s_f_add
            data.alter_value = v --变化值
            data.current_value = money[k] --最终结果
            g.sendLogs( "logs_chips", data)
        elseif v < 0 then
            data.lflag = g.const.chip.s_f_descr
            data.alter_value = math.abs(v)
            data.current_value = money[k]
            g.sendLogs( "logs_chips", data)
        end
    end
end

--清理空桌子
function Handler:clearIdleDesk()
    if g.desks and next(g.desks) then
        self.lock_desk = true
        local tbs = {}
        for k, v in pairs(g.desks) do
            if v.rtype == g.const.room_type.normal then
                local create_time = v.create_time
                if v.user_num == 0 and g.time() - create_time > 20*60 then
                    local tb = {num = v.user_num, tid=k}
                    table_insert(tbs, tb)
                end
            end
        end
        if #tbs > g.const.room.max_desk_num then
            for i=#tbs, 1 do
                local tb = tbs[i]
                if tb and next(tb) then
                    local tid = tb.tid
                    self:delDesk(tid)
                end
            end
        end
        self.lock_desk = false
    end
end

--创建桌子id,
--rid 房间id
--rtype 房间类型0普通， 1比赛
function Handler:createDeskId(rid, rtype)
    --没有就创建一个桌子
    self.index_id = self.index_id + 1
    if self.index_id > 10000 then
        self.index_id = 10
    end
    return  rtype.. "_".. rid.."_" ..self.index_id .."_" ..g.getNodeId() .."_" ..self.start_time
end

--创建一个桌子
function Handler:createDesk(rid, rtype)
    rtype = rtype or 0
    local tid = self:createDeskId(rid, rtype)
    local service = g.newService("games.texas.service", tid, rid, rtype)
    if not service then
        g.log:err("newService games.texas.service", tid, rid, rtype)
        return
    end
    --桌子对象
    g.desks[tid] = { service = service,
                     tid = tid,
                     rid = rid,
                     rtype = rtype,
                     user_num = 0,
                     seat_num = 0,
                     create_time = g.time(),
                     robot_num = 0,
    }
    g.call(service, "start")
    --if rtype == g.const.room_type.normal then
    --    --
    --    g.dp:setNodeTables(g.getNodeId(), tid)
    --end

    g.dp:setTables(tid)
    return g.desks[tid]
end

--
function Handler:createDeskSnooker(rid, rtype)
    rtype = rtype or 0
    local tid = self:createDeskId(rid, rtype)
    local service = g.newService("games.snooker.service", tid, rid, rtype)
    if not service then
        g.log:err("newService games.texas.service", tid, rid, rtype)
        return
    end
    --桌子对象
    g.desks[tid] = {
        service = service,
        tid = tid,
        rid = rid,
        rtype = rtype,
        user_num = 0,
        seat_num = 0,
        create_time = g.time(),
        robot_num = 0,
    }
    g.call(service, "start")
    g.dp:setTables(tid)
    return g.desks[tid]
end

function Handler:delDesk(tid)
    if not tid then return end
    self:callTable(tid, "close")
    g.desks[tid] = nil
    g.dp:delTables(tid)
end

--匹配一个桌子，普通场入桌
function Handler:reqEnterDesk(msg)
    g.log:info("reqEnterDesk: ", msg)
    if self.closed then
        g.log:warn("game is close!")
        return
    end
    if not msg.rtype and not msg.rid and not msg.tid then
        g.log:err("reqEnterDesk param err", msg)
        return {code=g.error_code.input_err}
    end
    local uid = msg.mid
    if not msg.rtype then
        msg.rtype = 0 --房间类型
    end
    if not uid then
        g.log:err("enterDesk uid err nil")
        return {code=g.error_code.invalid_mid}
    end
    if msg.tid and msg.tid ~= "" then
        --重连到指定桌子
        local str_tid = msg.tid
        local rtype = string.match(str_tid, "(%d)_.*") --这个是string类型
        local rid = string.match(str_tid, "%d_(%d+)_.*")
        local old_desk = self:getUserDesk(uid, rid, rtype)
        if old_desk then
            g.log:info("reqEnterDesk has old desk:", old_desk)
            msg.rtype = tonumber(rtype)
            msg.rid = tonumber(rid)
            msg.tid = old_desk.tid
            return self:enterDesk(old_desk, msg)
        end
        return self:enterDesk(g.desks[msg.tid], msg)
    end
    --在同一类型房间里只能进一个桌子
    local old_desk = self:getUserDesk(uid, msg.rid, msg.rtype)
    if old_desk then
        return self:enterDesk(old_desk, msg)
    end
    --最多只能进行两个桌子同时
    --取mid所在的桌子
    local tids = self:onGetUserDesks(uid)
    if #tids == 2 then
        if msg.tid and table.has(tids, msg.tid) then
        else
            return {code=g.error_code.in_too_tables}
        end
    end
    --如果正在删除空桌子先暂停分配
    for i=1, 3 do
        if self.lock_desk then
            g.sleep(100)
            g.log:debug("self.lock_desk is true")
        else
            break
        end
    end

    return g.manager:enterNormalDesk(msg)
end

function Handler:enterDesk(desk, msg)
    if not desk or not desk.service then
        --服务不存在
        g.log:err("enterDesk no service: ", desk, msg)
        return {code = g.error_code.game_over}
    end
    local uid = msg.mid
    local res = g.call(desk.service, "enter_desk", msg) --玩家进入桌子请求
    if res then
        self:bindUserDesk(uid, desk.tid)
    end
    return res
end

--绑定玩家与桌子
function Handler:bindUserDesk(uid, tid)
    if not g.user_desk[uid] then
        g.user_desk[uid] = {}
    end
    for _, v in pairs(g.user_desk[uid]) do
        if v == tid then
            --已经存在就不用插入了
            return
        end
    end
    table_insert(g.user_desk[uid], tid)
end

--解绑玩家离与桌子
function Handler:unbindUserDesk(uid, tid)
    local tb = g.user_desk[uid]
    if not tb then
        return
    end
    for k, v in pairs(tb) do
        if v == tid then
            print("unbindUserDesk#######", uid)
            table.remove(tb, k)
            return true
        end
    end
end

--在线信息
function Handler:reqOnline(msg)
    local rid = msg.rid
    local rtype = msg.type
    if not rid then
        rid = 0
    end
    if not rtype then
        rtype = 0
    end
    local info = g.manager:getOnlineInfo(rid, rtype)
    if g.getGameType() == g.const.game_type.texas then
        local cur_hour = tonumber(os.date("%H", g.time()))
        for k, v in pairs(info) do
            local num = self.robot_num_cfg[cur_hour][v.rid] or 0
            v.online = v.online + num
        end
    end
    return {code = g.error_code.ok, info = info}
end


--玩家在指定房间里的桌子
function Handler:getUserDesk(uid, rid, rtype)
    local tids = g.user_desk[uid] or {}
    g.log:info("getUserDesk tids:", uid, tids, rid, rtype)
    rid = tonumber(rid)
    rtype = tonumber(rtype)
    for _, tid in pairs(tids) do
        local desk = g.desks[tid]
        --g.log:warn("2222222222", desk.rid == rid , desk.rtype == rtype, desk.rid, rid , desk.rtype, rtype, type(desk.rid), type(rid) , type(desk.rtype), type(rtype) )
        if desk and desk.rid == rid and desk.rtype == rtype then
            return desk
        end
    end
end

--往桌子里发消息
function Handler:callTable(tid, ...)
    local tb = g.desks[tid]
    if not tb or not tb.service then
        --服务不存在
        g.log:warn("callTable no service: ", tid, ...)
        return {code = g.error_code.game_over}
    end
    return g.call(tb.service, ...)
end

function Handler:sendTable(tid, ...)
    local tb = g.desks[tid]
    if not tb or not tb.service then
        --服务不存在
        g.log:err("sendTable no service ", tid, ...)
        return
    end
    return g.send(tb.service, ...)
end


--------------------------------------------
---
--------------------------------------------
--默认游戏消息处理
function Handler:defaultCallback(msg_name, msg)
    if not msg_name or type(msg)~="table" or not msg or not msg.tid then
        g.log:error("msg or tid is nil", msg_name, msg)
        return
    end
    --if self.closed then
    --    g.log:warn("games closeed: ", msg_name, msg)
    --    return
    --end
    return self:callTable(msg.tid, msg_name, msg)
end

--玩家离开桌子
function Handler:onUserLeave(tid, uid)
    self:unbindUserDesk(uid, tid)
    --if g.desks[tid] and g.desks[tid].user_num > 0 then
    --    g.desks[tid].user_num = g.desks[tid].user_num - 1
    --end
end

--取玩家所在桌子
function Handler:onGetUserDesks(uid)
    local tids = {}
    local ok , res = g.callMatch("msg_to_match", "get_user_desks", uid)
    if ok and res then
        for _, tid in pairs(res) do
            table_insert(tids, tid)
        end
    end
    local ids = g.user_desk[uid] or {}
    for _, tid in pairs(ids) do
        if g.desks[tid] then
            table_insert(tids, tid)
        end
    end
    return tids
end

--玩家离线了
function Handler:onDisconnect(msg)
    local uid = msg.uid
    if not uid then return true end

    g.sendMatch("msg_to_match", "disconnect", msg)
    local tids = g.user_desk[uid]
    if not tids then return end
    if not next(tids) then --删除不在桌子的
        g.user_desk[uid] = nil
        return true
    end
    for _, tid in pairs(tids) do
        local tb = g.desks[tid]
        if not tb or not tb.service then
            --服务不存在
        else
            print("games disconnect:", tb.service, tid, uid)
            g.send(tb.service, "disconnect", msg)
        end
    end

    return true
end

--[[ desk = {
    service = service,
    id = tid,
    rid = rid,
    rtype=rtype,
    user_num = 0, 总人数
    seat_num
    }
--]]
--更新桌子信息
function Handler:onUpdateDesk(tid, msg)
    local desk = g.desks[tid]
    if not desk then return true end
    for k, v in pairs(msg) do
        desk[k] = v
    end
    return true
end

--关闭游戏
function Handler:onClose(desk_id)
    if desk_id and desk_id ~= "" then
        --关闭指定桌子
        for tid, _ in pairs(g.desks) do
            if desk_id == tid then
                self:delDesk(tid)
                break
            end
        end
    else
        --关闭所有游戏
        self.closed = true
        for tid, _ in pairs(g.desks) do
           self:delDesk(tid)
        end
    end
end

function Handler:onKickUser(msg)
    if not msg or type(msg)~="table" then return end
    local uid = msg.uid
    if not uid then return end
    local tids = g.user_desk[uid]
    if not tids then
        return
    end
    for _, tid in pairs(tids) do
        local tb = g.desks[tid]
        if not tb or not tb.service then
            --服务不存在
        else
            g.log:info("games onKickUser:", tb.service, tid, uid)
            g.send(tb.service, "kick_user", uid)
        end
    end
end

--匹配一个游戏(桌球
function Handler:reqEnterGame(msg)
    g.log:info("reqEnterGame: ", msg)
    if self.closed then
        g.log:warn("game is close!")
        return
    end
    if not msg.rtype and not msg.rid and not msg.tid then
        g.log:err("reqEnterGame param err", msg)
        return {code=g.error_code.input_err}
    end
    local uid = msg.mid
    if not msg.rtype then
        msg.rtype = 0 --房间类型
    end
    if not msg.gtype then
        msg.gtype = 0
    end
    if not uid then
        g.log:err("reqEnterGame uid err nil")
        return {code=g.error_code.invalid_mid}
    end
    if msg.tid and msg.tid ~= "" then
        --重连到指定桌子
        local str_tid = msg.tid
        local rtype = string.match(str_tid, "(%d)_.*") --这个是string类型
        local rid = string.match(str_tid, "%d_(%d+)_.*")
        local old_desk = self:getUserDesk(uid, rid, rtype)
        if old_desk then
            g.log:info("reqEnterDesk has old desk:", old_desk)
            msg.rtype = tonumber(rtype)
            msg.rid = tonumber(rid)
            msg.tid = old_desk.tid
            return self:enterDesk(old_desk, msg)
        end
        return self:enterDesk(g.desks[msg.tid], msg)
    end
    --在同一类型房间里只能进一个桌子
    local old_desk = self:getUserDesk(uid, msg.rid, msg.rtype)
    if old_desk then
        return self:enterDesk(old_desk, msg)
    end
    --最多只能进行两个桌子同时
    --取mid所在的桌子
    local tids = self:onGetUserDesks(uid)
    if #tids >= 2 then
        if msg.tid and table.has(tids, msg.tid) then
        else
            return {code=g.error_code.in_too_tables}
        end
    end
    --如果正在删除空桌子先暂停分配
    for i=1, 3 do
        if self.lock_desk then
            g.sleep(100)
            g.log:debug("self.lock_desk is true")
        else
            break
        end
    end

    return g.manager:enterRoom(msg)

end

function Handler:onUserLogin(uid)
    g.sendMatch("msg_to_match", "user_login", uid)
    --检查是否有没退还的金币
    local tids = g.user_desk[uid] or {}
    if next(tids) then
        return
    end
    local res = g.dp:getUserTableChips(uid)
    for tid, chip in pairs(res) do
        local moneys = {chips = chip}
        local ok = g.dp:addUserMoney(uid, moneys)
        if ok then
            g.log:warn("checkTableChip user addUserMoney:", uid, moneys)
            self:writeChipLog(uid, tid, moneys, g.const.chip.s_node_restart, "node_restart_back")
            --通知用户更新金币
            local data = {
                msg = cjson.encode({event="update_user"})
            }
            g.sendWatchdog( "TS_2C_eventBc", data)
        end
    end
    if next(res) then
        g.dp:delUserTableChips(uid)
    end
end






return Handler
